package com.xbongbong.saas.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.data.service.SerialNoService;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasFormDataEsModel;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.enums.EsAliasEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.ScreenTypeEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.ProSaveHelp;
import com.xbongbong.paas.help.SerialNoHelp;
import com.xbongbong.paas.log.MongoLogHelp;
import com.xbongbong.paas.log.constant.LogMemoConstant;
import com.xbongbong.paas.pojo.dto.FormDataAddDTO;
import com.xbongbong.paas.pojo.dto.FormDataRepeatDTO;
import com.xbongbong.paas.pojo.dto.SaasFormSaveDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.vo.FormDataRepeatVO;
import com.xbongbong.paas.service.PaasFormDataService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CloneUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.BuinessDefaultErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.TemplateErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.sheet.pojo.SheetAttrPojo;
import com.xbongbong.pro.sheet.pojo.dto.SheetSaveDTO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.constant.InvoiceConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ContractUserEntity;
import com.xbongbong.saas.domain.entity.InvoiceRelationshipEntity;
import com.xbongbong.saas.domain.entity.PaymentTaskUserEntity;
import com.xbongbong.saas.domain.entity.PurchaseInvoiceRelationshipEntity;
import com.xbongbong.saas.domain.entity.ext.PaymentSheetEntityExt;
import com.xbongbong.saas.enums.ExecutorCheckBoxEnum;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.RedundantTemplateTypeEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.PaymentSheetDistributorEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.ProductionOrderEnum;
import com.xbongbong.saas.model.ContractUserModel;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.model.PaymentTaskUserModel;
import com.xbongbong.saas.service.AssembleService;
import com.xbongbong.saas.service.BomBillService;
import com.xbongbong.saas.service.ClueService;
import com.xbongbong.saas.service.CommunicatePlanService;
import com.xbongbong.saas.service.CompetitorAnalysisService;
import com.xbongbong.saas.service.CompetitorService;
import com.xbongbong.saas.service.ContactService;
import com.xbongbong.saas.service.ContractService;
import com.xbongbong.saas.service.CostAdjustService;
import com.xbongbong.saas.service.CreditFixedLimitService;
import com.xbongbong.saas.service.CreditLimitService;
import com.xbongbong.saas.service.CreditTemporaryLimitService;
import com.xbongbong.saas.service.CustomerCommunicateService;
import com.xbongbong.saas.service.CustomerService;
import com.xbongbong.saas.service.DistributorAccountService;
import com.xbongbong.saas.service.DistributorCommunicateService;
import com.xbongbong.saas.service.DistributorContactService;
import com.xbongbong.saas.service.DistributorManageService;
import com.xbongbong.saas.service.FundTransferService;
import com.xbongbong.saas.service.InstockService;
import com.xbongbong.saas.service.InventoryService;
import com.xbongbong.saas.service.InvoiceService;
import com.xbongbong.saas.service.MarketActivityService;
import com.xbongbong.saas.service.OpportunityService;
import com.xbongbong.saas.service.OrderOutstockService;
import com.xbongbong.saas.service.OrderService;
import com.xbongbong.saas.service.OtherExpenseService;
import com.xbongbong.saas.service.OtherIncomeService;
import com.xbongbong.saas.service.OutstockService;
import com.xbongbong.saas.service.PayPlanService;
import com.xbongbong.saas.service.PayPlanSheetService;
import com.xbongbong.saas.service.PaymentService;
import com.xbongbong.saas.service.PaymentSheetService;
import com.xbongbong.saas.service.PaymentTaskService;
import com.xbongbong.saas.service.ProductService;
import com.xbongbong.saas.service.ProductionOrderService;
import com.xbongbong.saas.service.PurchaseInvoiceService;
import com.xbongbong.saas.service.PurchaseService;
import com.xbongbong.saas.service.QuotationService;
import com.xbongbong.saas.service.RefundService;
import com.xbongbong.saas.service.ReturnService;
import com.xbongbong.saas.service.ReturnedPurchaseService;
import com.xbongbong.saas.service.SupplierCommunicateService;
import com.xbongbong.saas.service.SupplierContactService;
import com.xbongbong.saas.service.SupplierService;
import com.xbongbong.saas.service.TransferService;
import com.xbongbong.saas.service.UserTeamService;
import com.xbongbong.saas.service.WarehouseService;
import com.xbongbong.saas.service.WorkOrderService;
import com.xbongbong.saas.service.WorkReportService;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.model.UserModel;
import com.xbongbong.util.ValidateUtil;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getDoubleOrDefaultFromFormData;
import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getJsonArrFromFormData;
import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getJsonArrOrDefaultFromFormData;
import static java.lang.String.format;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * @author haibing.zhang
 * @version v1.0
 * @date 2019/1/16
 * @since v1.0
 */
@Component
public class SaasSaveHelp {
    private static final Logger LOG = LoggerFactory.getLogger(SaasSaveHelp.class);

    @Resource
    private CustomerService customerService;
    @Resource
    private ContactService contactService;
    @Resource
    private ContractService contractService;
    @Resource
    private OutstockService outstockService;
    @Resource
    private OpportunityService opportunityService;
    @Resource
    private BomBillService bomBillService;
    @Resource
    private CommunicatePlanService communicatePlanService;
    @Resource
    @Lazy
    private CustomerCommunicateService customerCommunicateService;
    @Resource
    private WorkReportService workReportService;
    @Resource
    private InvoiceService invoiceService;
    @Resource
    private ProductionOrderService productionOrderService;
    @Resource
    private SupplierService supplierService;
    @Resource
    private SupplierContactService supplierContactService;
    @Resource
    private PurchaseService purchaseService;
    @Resource
    private ReturnedPurchaseService returnedPurchaseService;
    @Resource
    private PaymentService paymentService;
    @Resource
    private PayPlanService payPlanService;
    @Resource
    private SupplierCommunicateService supplierCommunicateService;
    @Resource
    private UserModel userModel;
    @Resource
    private InventoryService inventoryService;
    @Resource
    private ProductService productService;
    @Resource
    private TransferService transferService;
    @Resource
    private WarehouseService warehouseService;
    @Resource
    private PaasFormDataEsModel paasFormDataEsModel;
    @Resource
    private AssembleService assembleService;
    @Resource
    private InstockService instockService;
    @Resource
    private WorkOrderService workOrderService;
    @Resource
    private RefundService refundService;
    @Resource
    private PaymentSheetService paymentSheetService;
    @Resource
    private PayPlanSheetService payPlanSheetService;
    @Resource
    private QuotationService quotationService;
    @Resource
    private UserTeamService userTeamService;
    @Resource
    private PurchaseInvoiceService purchaseInvoiceService;
    @Resource
    private FundHelp fundHelp;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private MongoLogHelp mongoLogHelp;
    @Resource
    private PaymentTaskService paymentTaskService;
    @Resource
    private PaasFormDataService paasFormDataService;
    @Resource
    private ClueService clueService;
    @Resource
    private MarketActivityService marketActivityService;
    @Resource
    private SerialNoHelp serialNoHelp;
    @Resource
    private SerialNoService serialNoService;
    @Resource
    private ContractUserModel contractUserModel;
    @Resource
    private PaymentTaskUserModel paymentTaskUserModel;
    @Resource
    private CreditLimitService creditLimitService;
    @Resource
    private CreditTemporaryLimitService creditTemporaryLimitService;
    @Resource
    private CreditFixedLimitService creditFixedLimitService;
    @Resource
    private DistributorManageService distributorManageService;
    @Resource
    private DistributorContactService distributorContactService;
    @Resource
    private DistributorCommunicateService distributorCommunicateService;
    @Resource
    private OrderService orderService;
    @Resource
    private DistributorAccountService distributorAccountService;
    @Resource
    private OrderOutstockService orderOutstockService;
    @Resource
    private ReturnService returnService;
    @Resource
    private FundTransferService fundTransferService;
    @Resource
    private OtherIncomeService otherIncomeService;
    @Resource
    private OtherExpenseService otherExpenseService;
    @Resource
    private CostAdjustService costAdjustService;
    @Resource
    private IndexTypeModel indexTypeModel;
    @Resource
    private CompetitorService competitorService;
    @Resource
    private CompetitorAnalysisService competitorAnalysisService;

    /**
     * saas字段保存前特殊校验
     *
     * @param validateDataDTO 数据对象
     * @throws XbbException 抛出业务异常
     * @author haibin.zhang
     */
    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        Integer businessType = validateDataDTO.getBusinessType();
        businessType = XbbRefTypeEnum.transferBusinessType4Distributor(businessType, validateDataDTO.getDistributorMark());
        // 标签设置 todo
        dealLabelData(validateDataDTO.getData(), validateDataDTO.getExplainMap());
        if (Objects.equals(SaasMarkEnum.PAAS.getCode(), validateDataDTO.getSaasMark())
                && Objects.equals(validateDataDTO.getFromOuterLink(), 1)) {
            // paas外链不做校验
            return;
        }
        // todo
        if (Objects.equals(SaasMarkEnum.PAAS.getCode(), validateDataDTO.getSaasMark())) {
            userTeamService.beforeVerifyUserTeam(validateDataDTO);
            return;
        }
        if (businessType == null || businessType == 0) {
            LOG.error("businessType为空");
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002);
        }
        // 下方的beforeSave中，支持工作流的业务已在com.xbongbong.saas.help.AnalyticalHelp.beforeSave实现
        RedundantTemplateTypeEnum templateTypeEnum = RedundantTemplateTypeEnum.getByCode(businessType);
        switch (templateTypeEnum) {
            case CUSTOMER:
                customerService.beforeSave(validateDataDTO);
                if (CollectionsUtil.isNotEmpty(validateDataDTO.getOwnerIds()) || CollectionsUtil.isNotEmpty(validateDataDTO.getCoUserId()) || Objects.equals(validateDataDTO.getIsImport(), BasicConstant.ONE) || (!Objects.equals(validateDataDTO.getSubBusinessType(), XbbRefTypeEnum.CUSTOMER_PUBLIC.getCode()) && !Objects.equals(validateDataDTO.getFromOuterLink(), 1))) {
                    // 外链不校验团队，因为外链不支持人员处理
                    userTeamService.beforeVerifyUserTeam(validateDataDTO);
                }
                break;
            case CONTACT:
                contactService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case CONTRACT:
                contractService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case ORDER:
                orderService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case ASSEMBLE:
                assembleService.beforeSave(validateDataDTO);
                break;
            case OUTSTOCK:
            case CONTRACT_OUTSTOCK:
            case OTHER_OUTSTOCK:
            case RETURNED_PURCHASE_OUTSTOCK:
            case WORK_ORDER_OUTSTOCK:
            case PRODUCTION_MATERIEL_OUTSTOCK:
                outstockService.beforeSave(validateDataDTO);
                break;
            case INSTOCK:
            case PURCHASE_INSTOCK:
            case REFUND_INSTOCK:
            case OTHER_INSTOCK:
            case PRODUCTION_INSTOCK:
            case RETURNED_MATERIEL_INSTOCK:
                instockService.beforeSave(validateDataDTO);
                break;
            case SALES_OPPORTUNITY:
                opportunityService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case COMMUNICATE_PLAN:
                communicatePlanService.beforeSave(validateDataDTO);
                break;
            case COMMUNICATE:
                customerCommunicateService.beforeSave(validateDataDTO);
                break;
            case JXC_COMMUNICATE:
                supplierCommunicateService.beforeSave(validateDataDTO);
                break;
            case BOMBILL:
                bomBillService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case WORKREPORT_DAILY:
            case WORKREPORT_WEEKLY:
            case WORKREPORT_MONTHLY:
            case WORKREPORT:
                workReportService.beforeSave(validateDataDTO);
                break;
            case WORKORDER:
                workOrderService.beforeSave(validateDataDTO);
                if (Objects.equals(validateDataDTO.getFromOuterLink(), 1)) {
                    // 外链新建，因为没有负责人和协同人这两个，所以不需要校验
                    break;
                }
                //没有负责人单协同人
                //工单极其特殊的协同人负责人防重复校验（自由流程工单负责人，指定流程第一阶段为指定为人员的情况）
                userTeamService.beforeWorkOrderUserTeam(validateDataDTO);
                break;
            case INVOICE:
                invoiceService.beforeSave(validateDataDTO);
                break;
            case PURCHASE_INVOICE:
                purchaseInvoiceService.beforeSave(validateDataDTO);
                break;
            case PRODUCTIONORDER:
                productionOrderService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case PAYMENT:
                paymentService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case PAY_PLAN:
                payPlanService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case SUPPLIER:
                supplierService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case SUPPLIER_CONTACT:
                supplierContactService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case PURCHASE:
                purchaseService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case RETURNED_PURCHASE:
                returnedPurchaseService.beforeSave(validateDataDTO);
                break;
            case INVENTORY:
                inventoryService.beforeSave(validateDataDTO);
                break;
            case PRODUCT:
                productService.beforeSave(validateDataDTO);
                break;
            case TRANSFER:
                transferService.beforeSave(validateDataDTO);
                break;
            case WAREHOUSE:
                warehouseService.beforeSave(validateDataDTO);
                break;
            case PAYMENT_SHEET:
                paymentSheetService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case PAY_SHEET:
                payPlanSheetService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case REFUND:
                refundService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case QUOTATION:
                quotationService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case CLUE:
                clueService.beforeSave(validateDataDTO);
                if (CollectionsUtil.isNotEmpty(validateDataDTO.getOwnerIds()) || CollectionsUtil.isNotEmpty(validateDataDTO.getCoUserId()) || Objects.equals(validateDataDTO.getIsImport(), BasicConstant.ONE) || (!Objects.equals(validateDataDTO.getSubBusinessType(), XbbRefTypeEnum.CLUE_PUBLIC.getCode()) && !Objects.equals(validateDataDTO.getFromOuterLink(), 1))) {
                    userTeamService.beforeVerifyUserTeam(validateDataDTO);
                }
                break;
            case MARKET_ACTIVITY:
                marketActivityService.beforeSave(validateDataDTO);
                if (!Objects.equals(validateDataDTO.getFromOuterLink(), BasicConstant.ONE)) {
                    userTeamService.beforeVerifyUserTeam(validateDataDTO);
                }
                break;
            case CREDIT_LIMIT:
                creditLimitService.beforeSave(validateDataDTO);
                break;
            case CREDIT_TEMPORARY_LIMIT:
                creditTemporaryLimitService.beforeSave(validateDataDTO);
                break;
            case CREDIT_FIXED_LIMIT:
                creditFixedLimitService.beforeSave(validateDataDTO);
                break;
            case FUND_TRANSFER:
                fundTransferService.beforeSave(validateDataDTO);
                break;
            case OTHER_INCOME:
                otherIncomeService.beforeSave(validateDataDTO);
                break;
            case OTHER_EXPENSE:
                otherExpenseService.beforeSave(validateDataDTO);
                break;
            case COST_ADJUST:
                costAdjustService.beforeSave(validateDataDTO);
                break;
            case DISTRIBUTOR_MANAGEMENT:
                distributorManageService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case DISTRIBUTOR_CONTACT:
                distributorContactService.beforeSave(validateDataDTO);
                break;
            case DISTRIBUTOR_COMMUNICATE:
                distributorCommunicateService.beforeSave(validateDataDTO);
                break;
            case DISTRIBUTOR_ACCOUNT:
                distributorAccountService.beforeSave(validateDataDTO);
                break;
            case ORDER_OUTSTOCK:
                orderOutstockService.beforeSave(validateDataDTO);
                break;
            case RETURN:
                returnService.beforeSave(validateDataDTO);
                userTeamService.beforeVerifyUserTeam(validateDataDTO);
                break;
            case COMPETITOR:
                competitorService.beforeSave(validateDataDTO);
                break;
            case COMPETITOR_ANALYSIS:
                competitorAnalysisService.beforeSave(validateDataDTO);
                break;
            default:
                break;
        }
    }

    /**
     * 其他收入单、其他支出单公用的save处理部分：校验金额，时间等
     * @param validateDataDTO 数据对象
     * @param data 数据data
     * @param sheetTypeEnum 类型总枚举
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void otherBeforeSave(ValidateDataDTO validateDataDTO, JSONObject data, SheetTypeEnum sheetTypeEnum) throws XbbException {
        SaveFormatHelp.otherBeforeSave(validateDataDTO, data, sheetTypeEnum);
    }

    /**
     * 关联业务单选
     * 将前端传入的json对象格式转换成两个字段
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @param linkedAttr 格式化字段对应的隐藏字段
     * @throws XbbException 参数格式错误异常
     * @author chaoxiong.lei
     * @date 15:38 2019/1/21
     * @since v1.0
     **/
    public void formatLinkBusiness4Save(JSONObject data, String attr, String linkedAttr, Integer fieldType) throws XbbException {
        SaveFormatHelp.formatLinkBusiness4Save(data, attr, linkedAttr, fieldType);
    }

    /**
     * 关联业务单选
     * 将前端传入的json对象格式转换成两个字段
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @param linkedAttr 格式化字段对应的隐藏字段
     * @throws XbbException 参数格式错误异常
     * @author chaoxiong.lei
     * @date 15:38 2019/1/21
     * @since v1.0
     **/
    public void formatLinkBusiness4SaveByArray(JSONObject data, String attr, String linkedAttr, Integer fieldType) throws XbbException {
        SaveFormatHelp.formatLinkBusiness4SaveByArray(data, attr, linkedAttr, fieldType);
    }

    /**
     * 关联业务单选 name替换成流水号
     * 将前端传入的json对象格式转换成两个字段
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @param linkedAttr 格式化字段对应的隐藏字段
     * @throws XbbException 参数格式错误异常
     **/
    public void formatLinkBusiness4SaveBySerialNo(JSONObject data, String attr, String linkedAttr, Integer fieldType) throws XbbException {
        if (data == null || attr == null || linkedAttr == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
        }
        try {
            JSONArray jsonArray = data.getJSONArray(attr);
            if (jsonArray != null && !jsonArray.isEmpty()) {
                StringBuilder id = new StringBuilder();
                StringBuilder name = new StringBuilder();
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    id.append(jsonObject.getString(StringConstant.SAAS_LINK_BUSINESS_ID)).append(StringConstant.COMMA);
                    name.append(jsonObject.getString("serialNo")).append(StringConstant.COMMA);
                }
                // 去掉最后的","号
                String substringId = id.toString().substring(0, id.length() - 1);
                String substringName = name.toString().substring(0, name.length() - 1);

                if (Objects.equals(FieldTypeEnum.LINK_BUSINESS_MULTI.getType(), fieldType)) {
                    // 鲁江：下拉多选不要用逗号或者竖线分割，存成array
                    String[] substringIdArray = substringId.split(StringConstant.COMMA);
                    String[] substringNameArray = substringName.split(StringConstant.COMMA);
                    data.put(attr, substringIdArray);
                    data.put(linkedAttr, substringNameArray);
                } else {
                    data.put(attr, substringId);
                    data.put(linkedAttr, substringName);
                }
            }
        } catch (Exception e) {
            //   throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
        }
    }


    /**
     * 生日格式转换
     * 将前端传入的json对象格式转换成三个字段
     *
     * @param data                    前端传回的data数据
     * @param attr                    要格式化的字段attr
     * @param birthdayFlag      公历生日字段的隐藏字段attr
     * @param birthdayMmdd 阴历生日字段隐藏的字段attr
     * @throws XbbException 参数格式错误异常
     * @author chaoxiong.lei
     * @date 15:38 2019/1/21
     * @since v1.0
     **/
    public void formatBirthday4Save(JSONObject data, String attr, String birthdayFlag,String birthdayMmdd) throws XbbException {
        SaveFormatHelp.formatBirthday4Save(data, attr, birthdayFlag, birthdayMmdd);
    }

    /**
     * 将前端传入的json对象格式转换成两个字段__TextCheck
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @param linkedAttr 格式化字段对应的隐藏字段
     * @throws XbbException 参数格式错误异常
     * @author feng.zheng
     * @date 15:38 2019/1/31
     * @since v1.0
     **/
    public void formatTextCheck4Save(JSONObject data, String attr, String linkedAttr) throws XbbException {
        SaveFormatHelp.formatTextCheck4Save(data, attr, linkedAttr);
    }
    /**
     * 关联产品：解析前端传入的数据，获取产品数组
     * 关联产品：解析前端传入的数据，获取产品数组
     * "text_5":{
     *     "productList":[{
     *         "id":514,
     *         "num":2
     *     }, {
     *         "id":516,
     *         "num":2
     *     }]
     *     "others": {
     *          "surchargeList":[{
     *              "surchargeType": 2,
     *              "surchargeName": "优惠",
     *              "amount": 0
     *          },{
     *              "surchargeType": 1,
     *              "surchargeName": "我听过",
     *              "amount": 0
     *          }]
     *     }
     * }
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @throws XbbException 参数格式错误异常
     * @author feng.zheng
     * @date 15:38 2019/1/31
     * @since v1.0
     **/
    public JSONArray getProductArrayFromData(JSONObject data, String attr) throws XbbException {
        if (data == null || attr == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
        }
        try {
            JSONObject jsonObject = data.getJSONObject(attr);
            JSONArray productList = new JSONArray();
            if (jsonObject != null) {
                productList = jsonObject.getJSONArray(StringConstant.PRODUCT_LIST);
            }
            return productList;
        } catch (Exception e) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
        }
    }

    public JSONArray getProductionProduct(JSONObject data, String attr) throws XbbException {
        if (data == null || attr == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
        }
        try {
            JSONObject jsonObject = data.getJSONObject(attr);
            JSONArray productList = new JSONArray();
            if (jsonObject != null) {
                productList = jsonObject.getJSONArray(StringConstant.PRODUCT_LIST);
            }
            for (int i = 0; i < productList.size(); i++) {
                JSONObject json = productList.getJSONObject(i);
                if (Objects.equals(attr, ProductionOrderEnum.PRODUCT.getAttr())) {
                    json.put("type", 1);
                } else {
                    json.put("type", 2);
                }
            }
            return productList;
        } catch (Exception e) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
        }
    }

    /**
     * 关联产品：解析前端传入的数据，获取产品数组
     * "text_5":{
     *     "productList":[{
     *         "id":514,
     *         "num":2
     *     }, {
     *         "id":516,
     *         "num":2
     *     }]
     *     "others": {
     *          "surchargeList":[{
     *              "surchargeType": 2,
     *              "surchargeName": "优惠",
     *              "amount": 0
     *          },{
     *              "surchargeType": 1,
     *              "surchargeName": "我听过",
     *              "amount": 0
     *          }],
     *          "totalMoney": 123
     *     }
     * }
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @throws XbbException 参数格式错误异常
     * @author long.rao
     * @date 2019-03-05 15:28
     */
    public JSONArray getSurchargeArrayFromData(JSONObject data, String attr) throws XbbException {
        if (data == null || attr == null) {
            return new JSONArray();
        }
        try {
            JSONArray surchargeList = new JSONArray();
            if (data != null) {
                JSONObject others = data.getJSONObject(BusinessConstant.OTHERS);
                if (Objects.nonNull(others)) {
                    surchargeList = others.getJSONArray(StringConstant.SURCHARGE_LIST);
                }
            }
            return surchargeList;
        } catch (Exception e) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
        }
    }

    /**
     * 将前端传入的产品数组转换成id数组
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @throws XbbException 参数格式错误异常
     * @author haibin.zhang
     * @date 15:38 2019/10/15
     * @since v1.0
     **/
    public JSONArray formatSelectProduct4Save2(JSONObject data, String attr) throws XbbException {
        return SaveFormatHelp.formatSelectProduct4Save2(data, attr);
    }


    /**
     * 将前端传入的产品数组转换成id数组
     *
     * @param data       前端传回的data数据
     * @param attr       要格式化的字段attr
     * @throws XbbException 参数格式错误异常
     * @author haibin.zhang
     * @date 15:38 2019/10/15
     * @since v1.0
     **/
    public JSONArray formatProductSave(JSONObject data, String attr) throws XbbException {
        if (data == null || attr == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
        }
        try {
            JSONArray productArray = data.getJSONArray(attr);
            if (productArray == null) {
                return new JSONArray();
            }
            List<Long> ids = new ArrayList<>();
            for (int i = 0; i < productArray.size(); i++) {
                JSONObject jsonObject = productArray.getJSONObject(i);
                if (jsonObject != null) {
                    Long productId = jsonObject.getLong("dataId");
                    ids.add(productId);
                }
            }
            data.put(attr, ids);
            return productArray;
        } catch (Exception e) {
            data.put(attr, new JSONArray());
            return new JSONArray();
        }
    }

    public void verifyTeamIsSame(SaasFormSaveDTO saasFormSaveDTO, String corpid) throws XbbException {
        // 新的负责人
        List<String> newMainUserList = new ArrayList<>();
        if (saasFormSaveDTO.getOwnerId() != null) {
            saasFormSaveDTO.getOwnerId().forEach(owner-> newMainUserList.add(Objects.toString(owner.getId(), "")));
        }
        List<String> copyNewMainUserList = (List<String>)CloneUtil.deepClone(newMainUserList);
        // 新的协同人
        List<String> newCoUserList = new ArrayList<>();
        if (saasFormSaveDTO.getCoUserId() != null) {
            saasFormSaveDTO.getCoUserId().forEach(coUser-> newCoUserList.add(Objects.toString(coUser.getId(), "")));
        }
        copyNewMainUserList.retainAll(newCoUserList);
        if (CollectionsUtil.isNotEmpty(copyNewMainUserList)) {
            List<UserEntity> userList = userModel.getByUserIdIn(corpid, copyNewMainUserList, true);
            StringBuilder errorUserName = new StringBuilder();
            String errorUser = "";
            for (UserEntity userEntity : userList) {
                errorUserName.append(userEntity.getName()).append(StringConstant.COMMA);
            }
            if (!errorUserName.toString().isEmpty()) {
                errorUser = errorUserName.substring(0, errorUserName.length() - 1);
            }
            throw new XbbException(BuinessDefaultErrorCodeEnum.API_ERROR_200024, String.format(BuinessDefaultErrorCodeEnum.API_ERROR_200024.getMsg(), errorUser));
        }
    }

    /**
     * 将前端传入的json对象格式转换成两个字段
     *
     * @param data                    前端传回的data数据
     * @param attr                    要格式化的字段attr
     * @param communicateBusinessType 格式化字段对应的隐藏字段
     * @throws XbbException 参数格式错误异常
     * @author feng.zheng
     * @date 15:38 2019/1/31
     * @since v1.0
     **/
    public void formatCommunicateBusiness4Save(JSONObject data, String attr, String communicateBusinessType, String communicateBusinessLinkText) throws XbbException {
        SaveFormatHelp.formatCommunicateBusiness4Save(data, attr, communicateBusinessType, communicateBusinessLinkText);
    }


    /**
     * 校验编号长度且编号存在
     *
     * @param serialNumber 流水号
     * @param sheetNoAttrName 流水号名称
     * @author feng.zheng
     * @date 2019/2/14 22:05
     */
    public void checkSerialNumber(String serialNumber, String sheetNoAttrName) throws XbbException {
        ValidateUtil.checkSerialNumber(serialNumber, sheetNoAttrName);
    }

    /**
     * 校验字段长度
     *
     * @param data 数据
     * @param attr 属性
     * @param attrName 字段名
     * @param length 字段长度
     * @author feng.zheng
     * @date 2019/2/15 20:23
     */
    public void checkFieldEntityLength(JSONObject data, String attr, String attrName, Integer length) throws XbbException {
        ValidateUtil.checkFieldEntityLength(data, attr, attrName, length);
    }

    /**
     * 根据查询条件判断判重（bom版本判重，子产品条码查重等）
     *
     * @param formDataRepeatDTO 查重条件dto
     * @param conditionList 查重条件
     * @author chaoxiong.lei
     * @date 17:15 2019/2/19
     * @since v1.0
     **/
    public FormDataRepeatVO isUniqueByConditions(FormDataRepeatDTO formDataRepeatDTO, List<ConditionsEntityExt> conditionList) throws XbbException {
        return commonHelp.isUniqueByConditions(formDataRepeatDTO, conditionList);
    }

    /**
     * 根据查询条件判断判重（精确匹配可为空）
     *
     * @param formDataRepeatDTO 查重入参
     * @param conditionList 控制条件
     * @author feng.zheng
     * @date 17:15 2019/2/19
     * @since v1.0
     **/
    public FormDataRepeatVO repeatOnlyByConditions(FormDataRepeatDTO formDataRepeatDTO, List<ConditionsEntityExt> conditionList) throws XbbException {
        FormDataRepeatVO formDataRepeatVO;
        try {
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = boolQuery();

            // 业务类型
            IndexTypeEnum indexTypeEnum = indexTypeModel.getIndexType(formDataRepeatDTO.getCorpid(), formDataRepeatDTO.getSaasMark(), formDataRepeatDTO.getBusinessType());
            // 解析查询条件
            EsUtil.parseCondition(boolQueryBuilder, conditionList, null,null,null);
            // 拼接appId，菜单id等数据
            EsUtil.getTermQuery(boolQueryBuilder, formDataRepeatDTO, true);
            // 有精确匹配的属性查重
            if (formDataRepeatDTO.getAttr() != null && formDataRepeatDTO.getValue() != null) {
                String attr = "data." + formDataRepeatDTO.getAttr();
                if(StringUtil.isNotEmpty(formDataRepeatDTO.getSubAttr())) {
                    attr = attr + "." + formDataRepeatDTO.getSubAttr();
                }
                attr = attr + ".keyword";
                boolQueryBuilder.filter(termQuery(attr, formDataRepeatDTO.getValue()));
            }
             if (indexTypeEnum == IndexTypeEnum.UNKNOWN) {
                throw new XbbException(TemplateErrorCodeEnum.API_ERROR_350006);
            }
            boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
            SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
            //TODO 没找到page相关的代码，默认查询20条 2019年6月27日11:08:03
            PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, 10);
            sourceBuilder.query(boolQueryBuilder);
            searchRequest.source(sourceBuilder);
            formDataRepeatVO = paasFormDataEsModel.repeat(pageRequest,searchRequest);
        }catch (Exception e) {
            LOG.error("repeatOnlyByConditions查询数据重复出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return formDataRepeatVO;
    }

    /**
     * 获取流水号
     *
     * @param explainList 解释
     * @param formId 表单id
     * @param corpid 公司id
     * @return 流水号
     * @throws XbbException 异常
     */
    public String getSerialNo (List<FieldAttrEntity> explainList, Long formId, String corpid) throws XbbException {
        String serialNo = "";
        for (FieldAttrEntity fieldAttrEntity : explainList) {
            serialNo = getSerialNo(serialNo, corpid, formId, fieldAttrEntity);
        }
        return serialNo;
    }

    /**
     * 生成流水号
     * @param serialNo 回款单编号
     * @param corpid 公司id
     * @param formId 表单id
     * @param fieldAttrEntity 字段实体
     * @return java.lang.String
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public String getSerialNo(String serialNo, String corpid, Long formId, FieldAttrEntity fieldAttrEntity) throws XbbException {
        return serialNoHelp.getSerialNo(serialNo, corpid, formId, fieldAttrEntity);
    }

    /**
     * 生成流水号
     * @param corpid 公司id
     * @param formId 表单id
     * @param fieldAttrEntity 字段实体
     * @return java.lang.String
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public String getSerialNo(String corpid, Long formId, FieldAttrEntity fieldAttrEntity) throws XbbException {
        return serialNoService.getSerialNo(corpid, formId, fieldAttrEntity);
    }

    /**
     * 开启严控模式后，生成相应的流水号
     * @param formDataSaveDTO
     * @param corpid
     * @param fieldAttrEntity
     * @throws XbbException
     */
    public String getSerialNo(String serialNo, FormDataAddDTO formDataSaveDTO, String corpid, FieldAttrEntity fieldAttrEntity) throws XbbException {
        // 校验流水号是否重复
        Boolean flag = false;
        while (!flag) {
            serialNo = getSerialNo(serialNo, corpid, formDataSaveDTO.getFormId(), fieldAttrEntity);
            // 校验流水号是否重复
            formDataSaveDTO.setSerialNo(serialNo);
            FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
            BeanUtil.copyProperties(formDataSaveDTO, formDataRepeatDTO);
            formDataRepeatDTO.setSerialNo(serialNo);
            formDataRepeatDTO.setValue(serialNo);
            flag = paasFormDataService.repeat(formDataRepeatDTO).getNoRepeat();
        }
        return serialNo;
    }
    /**
     * 设置默认值
     * @param validateDataDTO 入参
     * @author zhouwq
     * @date 2019/6/24 11:18
     */
    public void initDefaultValue(ValidateDataDTO validateDataDTO){
        JSONObject data = validateDataDTO.getData();
        Map<String, FieldAttrEntity> entityMap = validateDataDTO.getExplainMap();

        entityMap.forEach((key, fieldAttrEntity) -> {
            if(!data.containsKey(key)) {
                Integer fieldType = fieldAttrEntity.getFieldType();
                boolean inData = EsUtil.setInData(fieldType);
                //系统字段不在data内跳过赋默认值步骤
                if(!inData){
                    return;
                }
                //不展示字段跳过赋默认值步骤
                if (fieldAttrEntity.getScreenType() == null || fieldAttrEntity.getScreenType() > ScreenTypeEnum.SCREEN_OFF.getType()) {
                    return;
                }
                FieldTypeEnum fieldTypeEnum = FieldTypeEnum.getFieldTypeEnum(fieldType);
                if (fieldTypeEnum != null) {
                    String esAlias = fieldTypeEnum.getEsalias();
                    EsAliasEnum esAliasEnum = EsAliasEnum.getByEsAlias(esAlias);
                    //设置默认值
                    if (esAliasEnum != null) {
                        switch (esAliasEnum) {
                            case NUM:
                            case DATE:
                                data.put(key, 0);
                                break;
                            case ARRAY:
                                data.put(key, new String[0]);
                                break;
                            case TEXT:
                                data.put(key, "");
                            default:
                                break;
                        }
                    }
                }
            }
        });
    }

    /**
     * 校验金额字段
     * @author 海镔
     * @date 2019/9/24 20:23
     * @param data 数据
     * @param attr 字段标识
     * @param explainMap 字段容器
     * @return void
     * @throws XbbException 业务异常
     */
    public void validatePayment(JSONObject data, String attr, Map<String, FieldAttrEntity> explainMap) throws XbbException {
       ValidateUtil.validatePayment(data, attr, explainMap);
    }

    public void getIdsByData(JSONArray objects,List<Object> list){
        for(int i=0;i < objects.size();i++){
            if(objects.get(i) instanceof String){
                String id = objects.getString(i);
                list.add(Long.valueOf(id));
            }else if(objects.get(i) instanceof Integer){
                Integer id = objects.getInteger(i);
                list.add(id);
            }else {
                JSONObject jsonObject = objects.getJSONObject(i);
                Long id = jsonObject.getLong(BasicConstant.ID);
                list.add(id);
            }

        }
    }

    public Map<Long,PaymentSheetEntityExt> getMoreContractIdMapFromPaymentSheet(List<PaymentSheetEntityExt> paymentSheetEntityExtList,Map<String,List<Long>> subPaymentSheetMap){
        Map<Long,PaymentSheetEntityExt> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if(CollectionsUtil.isNotEmpty(paymentSheetEntityExtList)) {
            for (PaymentSheetEntityExt paymentSheetEntity : paymentSheetEntityExtList) {
                    checkNullTwoInvoiceAmount(paymentSheetEntity);
                    if(Objects.equals(paymentSheetEntity.getAlone(), SheetAloneEnum.CHILD.getCode())){
                        addMapTwoId(subPaymentSheetMap,paymentSheetEntity.getUuid(),paymentSheetEntity.getId());
                    }
                    paymentSheetEntity.getData().put(InvoiceConstant.SUB_PAYMENT_SHEET_IDS,subPaymentSheetMap.get(paymentSheetEntity.getUuid()));
                    map.put(paymentSheetEntity.getData().getJSONArray(PaymentSheetEnum.CONTRACT.getAttr()).getLong(0),paymentSheetEntity);
                }
            }
        return map;
    }

    public Map<Long,List<PaymentSheetEntityExt>> getOneContractIdMapFromPaymentSheet(List<PaymentSheetEntityExt> paymentSheetEntityExtList,Map<String,List<Long>> subPaymentSheetMap){
        Map<Long,List<PaymentSheetEntityExt>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<PaymentSheetEntityExt> paymentSheetEntityExts = new ArrayList<>();
        Long contractId = null;
        if(CollectionsUtil.isNotEmpty(paymentSheetEntityExtList)) {
            for (PaymentSheetEntityExt paymentSheetEntity : paymentSheetEntityExtList) {
                checkNullTwoInvoiceAmount(paymentSheetEntity);
                if(Objects.equals(paymentSheetEntity.getAlone(), SheetAloneEnum.CHILD.getCode())){
                   paymentSheetEntityExts.add(paymentSheetEntity);
                    contractId = paymentSheetEntity.getData().getJSONArray(PaymentSheetEnum.CONTRACT.getAttr()).getLong(0);
                }
            }
            map.put(contractId,paymentSheetEntityExts);
        }
        return map;
    }

    public Map<Long,String> getUuidByPaymentSheet(List<PaasFormDataEntityExt>  paasFormDataESList){
        Map<Long,String> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if(CollectionsUtil.isNotEmpty(paasFormDataESList)) {
            for (PaasFormDataEntityExt paasFormDataEntityExt : paasFormDataESList) {
                if (Objects.nonNull(paasFormDataEntityExt.getUuid())) {
                    map.put(paasFormDataEntityExt.getId(),paasFormDataEntityExt.getUuid());
                }
            }
        }
        return map;
    }

    public void checkNullTwoInvoiceAmount(PaasFormDataEntityExt paymentSheetEntity){
        JSONObject dataObject = paymentSheetEntity.getData();
        JSONObject sourceData = paymentSheetEntity.getSourceData();
        if (Objects.nonNull(dataObject)) {
            if (!dataObject.containsKey(PaymentSheetEnum.INVOICE_AMOUNT.getAttr())) {
                dataObject.put(PaymentSheetEnum.INVOICE_AMOUNT.getAttr(), 0D);
                if (Objects.nonNull(sourceData)) {
                    sourceData.put(PaymentSheetEnum.INVOICE_AMOUNT.getAttr(), 0D);
                }
            }else if(dataObject.containsKey(PaymentSheetEnum.INVOICE_AMOUNT.getAttr())){
                if (Objects.isNull(dataObject.getDouble(PaymentSheetEnum.INVOICE_AMOUNT.getAttr()))) {
                    dataObject.put(PaymentSheetEnum.INVOICE_AMOUNT.getAttr(), 0D);
                    if (Objects.nonNull(sourceData)) {
                        sourceData.put(PaymentSheetEnum.INVOICE_AMOUNT.getAttr(), 0D);
                    }
                }
            }
        }
    }

    public void addMapTwoId(Map<String,List<Long>> map,String uuid,Long id){
        if(map.containsKey(uuid)){
            map.get(uuid).add(id);
        }else {
            List<Long> subIdList = new ArrayList<>();
            subIdList.add(id);
            map.put(uuid,subIdList);
        }
    }

    public void addMapTwoSerialNo(Map<String,List<String>> map,String uuid,String serialNo){
        if(map.containsKey(uuid)){
            map.get(uuid).add(serialNo);
        }else {
            List<String> subSerialNoList = new ArrayList<>();
            subSerialNoList.add(serialNo);
            map.put(uuid,subSerialNoList);
        }
    }

    public void addMapTwoInvoiceAmount(Map<String,Double> map,String uuid,Double invoiceAmount){
        if(Objects.nonNull(map)) {
            if (map.containsKey(uuid)) {
                map.put(uuid, Arith.add(map.getOrDefault(uuid,0D),invoiceAmount));
            } else {
                map.put(uuid, invoiceAmount);
            }
        }
    }

    public void addMapNormal(Map<Long,Double> map,Long id,Double amount){
        AddMapHelp.addMapNormal(map, id, amount);
    }

    public void addMapCount(Map<Long,Integer> map,Long id){
        if(Objects.nonNull(map)){
            if(map.containsKey(id)){
                map.put(id,map.getOrDefault(id,0)+BasicConstant.ONE);
            }else {
                map.put(id,BasicConstant.ONE);
            }
        }
    }

    /**
     * 发票关系List
     * @param relationshipMap
     * @param invoiceRelationshipEntity
     */
    public void putInvoiceRelationshipMap(Map<Long,List<InvoiceRelationshipEntity>> relationshipMap, InvoiceRelationshipEntity invoiceRelationshipEntity){
        if(relationshipMap.containsKey(invoiceRelationshipEntity.getInvoiceId())){
            List<InvoiceRelationshipEntity> invoiceRelationshipEntities = relationshipMap.getOrDefault(invoiceRelationshipEntity.getInvoiceId(),new ArrayList<>());
            invoiceRelationshipEntities.add(invoiceRelationshipEntity);
        }else {
            List<InvoiceRelationshipEntity> invoiceRelationshipEntities = new ArrayList<>();
            invoiceRelationshipEntities.add(invoiceRelationshipEntity);
            relationshipMap.put(invoiceRelationshipEntity.getInvoiceId(),invoiceRelationshipEntities);
        }
    }

    /**
     * 发票关系List
     * @param relationshipMap
     * @param purchaseInvoiceRelationshipEntity
     */
    public void putPurchaseInvoiceRelationshipMap(Map<Long,List<PurchaseInvoiceRelationshipEntity>> relationshipMap, PurchaseInvoiceRelationshipEntity purchaseInvoiceRelationshipEntity){
        if(relationshipMap.containsKey(purchaseInvoiceRelationshipEntity.getPurchaseInvoiceId())){
            List<PurchaseInvoiceRelationshipEntity> purchaseInvoiceRelationshipEntities = relationshipMap.getOrDefault(purchaseInvoiceRelationshipEntity.getPurchaseInvoiceId(),new ArrayList<>());
            purchaseInvoiceRelationshipEntities.add(purchaseInvoiceRelationshipEntity);
        }else {
            List<PurchaseInvoiceRelationshipEntity> purchaseInvoiceRelationshipEntities = new ArrayList<>();
            purchaseInvoiceRelationshipEntities.add(purchaseInvoiceRelationshipEntity);
            relationshipMap.put(purchaseInvoiceRelationshipEntity.getPurchaseInvoiceId(),purchaseInvoiceRelationshipEntities);
        }
    }

    public void getRefAmountByEsList(List<PaasFormDataEntityExt> list,Map<Long,Double> map,Map<Long,Double> amountMap,XbbRefTypeEnum xbbRefTypeEnum) throws XbbException{
        if(CollectionsUtil.isNotEmpty(list)) {
            for (PaasFormDataEntityExt paasFormDataEntityExt : list) {
                JSONObject data = paasFormDataEntityExt.getData();
                JSONArray array = null;
                //老发票的合并开票逻辑是直接开完,开票金额就是关联数据的
                if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.CONTRACT)) {
                   array =  FastJsonHelper.getJsonArrOrDefaultFromFormData(data, InvoiceEnum.CONTRACT_ID.getAttr(),new JSONArray());
                }
                if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAYMENT)) {
                    array =  FastJsonHelper.getJsonArrOrDefaultFromFormData(data, InvoiceEnum.PAYMENT_ID.getAttr(),new JSONArray());
                }
                if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAYMENT_SHEET)) {
                    array =  FastJsonHelper.getJsonArrOrDefaultFromFormData(data, InvoiceEnum.PAYMENT_SHEET_ID.getAttr(),new JSONArray());
                }
                getOldInvoiceAmount(array,map,amountMap,data);
            }
        }
    }

    public void getOldInvoiceAmount(JSONArray array,Map<Long,Double> map,Map<Long,Double> amountMap,JSONObject data) throws XbbException{
        if(CollectionsUtil.isNotEmpty(array)) {
            if (array.size() > 1) {
                for (int i = 0; i < array.size(); i++) {
                    Long id = array.getLong(i);
                    //注意这里的红冲数据要取负值
                    double invoiceAmount = amountMap.getOrDefault(id, 0D);
                    addMapNormal(map, id, -invoiceAmount);
                }
            } else if (Objects.equals(array.size(), BasicConstant.ONE)) {
                Long id = array.getLong(0);
                double invoiceAmount = getDoubleOrDefaultFromFormData(data, InvoiceEnum.TOTAL_MONEY.getAttr(), 0D);
                addMapNormal(map, id, invoiceAmount);
            }
        }
    }
    /**
     *
     * 生成出入库单
     * @param saasFormSaveDTO 数据
     * @author jyh
     * @date 2020/1/19 10:30
     * @since v1.0
     *
     */
    public void generateStockSheet(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        SaasFormSaveDTO saveDTO = (SaasFormSaveDTO) CloneUtil.deepClone(saasFormSaveDTO);
        // 生成出库单
        outstockService.generateOutStockSheet(saasFormSaveDTO);
        // 生成入库单
        instockService.generateInStockSheet(saveDTO);
    }



    /**
     * 处理回/付款单的明细：当小于等于1条时，不需要处理；大于1条，则把子的合同
     * @param sheetAmount 回/付款单总金额
     * @param sheetTypeEnum 回/付款单类型
     * @param sheetAttrPojo 回/付款单的字段attr pojo
     * @param dataList 父回/付款单数据data
     * @param amountDetails 明细数组
     * @param allAmount 总金额
     * @param contractIdToAmount 合同id-合同已收金额（采购合同id-采购合同已收金额）
     * @param paymentIdToAmount 回/付款计划id-回/付款计划已收金额
     * @param contractIds （采购）合同id集合
     * @param paymentIds 回/付款计划id集合
     * @return java.util.List<com.alibaba.fastjson.JSONObject>
     * @throws XbbException
     * @author zcp
     * @date 19/11/21 021 20:55
     * @update 19/11/21 021 20:55
     * @since v1.0
     * @version v1.0
     */
    public List<JSONObject> handleAmountDetail(SheetSaveDTO sheetSaveDTO, Double sheetAmount, SheetTypeEnum sheetTypeEnum, SheetAttrPojo sheetAttrPojo, JSONObject dataList, JSONArray amountDetails, double allAmount, Map<Long, Double> contractIdToAmount, Map<Long, Double> paymentIdToAmount, Set<Long> contractIds, Set<Long> paymentIds) throws XbbException {
        /*
         把回/付款单本身的金额处理掉（置为负数、以及处理回/付款金额、核销金额）
         非明细，则要根据回/付款单类型取金额值：预收款核销使用的是核销金额
         */

        dataList = fundHelp.formatAmountWriteOff(null, sheetTypeEnum, sheetAttrPojo, dataList);
        dealLabelData(dataList, sheetSaveDTO.getExplainMap());
        Integer distributorMark = sheetSaveDTO.getDistributorMark();
        //处理经销商支付方式
        String payMethod = handlePayMethod4Distributor(sheetSaveDTO.getPlatform(), dataList, sheetTypeEnum.getCode(), distributorMark);
        List<JSONObject> sonPaymentSheetDataList = new ArrayList<>();
        if (Objects.nonNull(amountDetails)) {
            for (Object item : amountDetails) {
                JSONObject jsonObject = (JSONObject) item;
                dealLabelData(jsonObject, sheetSaveDTO.getExplainMap());
            }
        }
        //如果明细只有一条，则只有父本身
        if (amountDetails == null || amountDetails.size() <= 1) {
            //处理归属人
            JSONArray signUserArr = getJsonArrOrDefaultFromFormData(dataList, sheetAttrPojo.getAttrBelongId(), new JSONArray());
            //防止无归属人时，前端传[null]过来
            signUserArr.remove(null);
            if (signUserArr.size() == 0) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.NO_BELONG_AMOUNT_DETAIL);
            } else if (signUserArr.size() > 1) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208047, format(PaymentErrorCodeEnum.API_ERROR_208047.getMsg(), sheetTypeEnum.getXbbRefTypeEnum().getName()));
            }
            //没有明细时，则把父本身的关联数据置入相应容器，且限制归属人只能一个
            handleIdAmount4NoDetail(dataList, sheetAttrPojo.getAttrCon(), sheetAmount, contractIdToAmount, contractIds);
            handleIdAmount4NoDetail(dataList, sheetAttrPojo.getAttrPlan(), sheetAmount, paymentIdToAmount, paymentIds);
        } else {
            //明细内有金额的合同、回/付款计划、归属人
            List<Long> detailContractIds = new ArrayList<>();
            List<String> detailContractNos = new ArrayList<>();
            List<Long> detailPaymentIds = new ArrayList<>();
            List<String> detailPaymentNos = new ArrayList<>();
            //外部归属人必须只能是明细内有金额的归属人，且要排重
            Set<Object> detailSignPerson = new HashSet<>();
            ValidateDataDTO validateDataDTO = new ValidateDataDTO();
            validateDataDTO.setBusinessType(sheetTypeEnum.getBusinessType());
            validateDataDTO.setExplainMap(sheetSaveDTO.getExplainMap());
            validateDataDTO.setNonCheckAmount(true);
            boolean payPlanSheetFlag = Objects.equals(XbbRefTypeEnum.PAY_SHEET, sheetTypeEnum.getXbbRefTypeEnum()) ? true : false;
            for(Object obj : amountDetails){
                JSONObject jsonObject = (JSONObject)obj;
                Double amount = jsonObject.getDouble(sheetAttrPojo.getAttrAmount());
                if(amount != null && amount != 0D) {
                    validateDataDTO.setData(jsonObject);
                    ProSaveHelp.analyticalData(validateDataDTO);
                    //处理关联客户、合同、回/付款计划、红冲回/付款单id
                    if (payPlanSheetFlag) {
                        payPlanSheetService.beforeSave(validateDataDTO);
                    } else {
                        paymentSheetService.beforeSave(validateDataDTO);
                    }
                    //处理归属人
                    List<Object> signUserIds = ProSaveHelp.getIdList(jsonObject.getJSONArray(sheetAttrPojo.getAttrBelongId()));
                    //防止无归属人时，前端传[null]过来
                    signUserIds.remove(null);
                    if (signUserIds.size() == 0) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.NO_BELONG_AMOUNT_DETAIL);
                    } else if (signUserIds.size() > 1) {
                        throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208047, format(PaymentErrorCodeEnum.API_ERROR_208047.getMsg(), sheetTypeEnum.getXbbRefTypeEnum().getName()));
                    }
                    jsonObject.put(sheetAttrPojo.getAttrBelongId(), signUserIds);
                    detailSignPerson.addAll(signUserIds);
                    allAmount = Arith.add(allAmount, amount);
                    /*
                     把明细的金额处理掉（置为负数、以及处理回/付款金额、核销金额）
                     前端传入的明细，不区分回/付款单类型，恒用的num_1，因此此处传入回/付款单的AMOUNT.getAttr()
                     */
                    jsonObject = fundHelp.formatAmountWriteOff(amount, sheetTypeEnum, sheetAttrPojo, jsonObject);
                    //处理合同，拼到父回/付款的detailContractIds、detailContractNos上
                    handleDetailIdName(jsonObject, sheetAttrPojo.getAttrCon(), sheetAttrPojo.getAttrConLink(), detailContractIds, detailContractNos, contractIdToAmount, contractIds, amount);
                    //处理回/付款，拼到父回/付款的detailPaymentIds、detailPaymentNos上
                    handleDetailIdName(jsonObject, sheetAttrPojo.getAttrPlan(), sheetAttrPojo.getAttrPlanLink(), detailPaymentIds, detailPaymentNos, paymentIdToAmount, paymentIds, amount);
                    //处理子回款单的支付方式
                    handlePayMethod4Distributor4SonSheet(jsonObject, distributorMark, payMethod);
                    sonPaymentSheetDataList.add(jsonObject);
                }
            }
            //判断明细总金额和外面输入的是否相等
            if (!Objects.equals(allAmount, sheetAmount)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.NOT_EQUAL_AMOUNT_DETAIL);
            }
            dataList.put(sheetAttrPojo.getAttrCon(), detailContractIds);
            dataList.put(sheetAttrPojo.getAttrConLink(), detailContractNos);
            dataList.put(sheetAttrPojo.getAttrPlan(), detailPaymentIds);
            dataList.put(sheetAttrPojo.getAttrPlanLink(), detailPaymentNos);
            dataList.put(sheetAttrPojo.getAttrBelongId(), detailSignPerson);
        }
        return sonPaymentSheetDataList;
    }

    /**
     * 记录回/付款单保存操作日志
     * @param sheetSaveDTO 回/付款单保存dto
     * @param corpid 公司id
     * @param userId 员工id
     * @param sheetEntityExt 回/付款单实体
     * @param typeName 名称
     * @return java.lang.String
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public String sheetAddLog(SheetSaveDTO sheetSaveDTO, String corpid, String userId, PaasFormDataEntity sheetEntityExt, String typeName, OperateModuleTypeEnum operateModuleTypeEnum) throws XbbException {
        boolean isImport = Objects.equals(1, sheetSaveDTO.getIsImport());
        if (!isImport) {
            // 记录日志
            String creatorId = sheetEntityExt.getCreatorId();
            Integer flowStatus = sheetSaveDTO.getFlowStatus();
            String creatName = "";
            if (creatorId != null) {
                // 走审批时获取创建人id
                UserEntity user = userModel.getByKeyIngoreDel(creatorId, corpid);
                if (Objects.nonNull(user)) {
                    creatName = user.getName();
                } else {
                    creatName = "***(" + I18nMessageUtil.getMessage(I18nStringConstant.RESIGNED)+ ")";
                }
            }
            String loginUserName = sheetSaveDTO.getLoginUserName();
            if (Objects.equals(flowStatus, FlowStatusEnum.PASS.getType()) && Objects.nonNull(creatorId)) {
                loginUserName = creatName;
                userId = creatorId;
            }
            String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_ADD_WORK), loginUserName, typeName, sheetEntityExt.getSerialNo());
            mongoLogHelp.buildLog(corpid, userId, loginUserName, operateModuleTypeEnum, OperateTypeEnum.NEW,
                    sheetEntityExt.getId().toString(), sheetEntityExt.getSerialNo(), memo, sheetSaveDTO.getHttpHeader());
        }
        return userId;
    }

    /**
     * 经销商类型回款单，厂商端操作需后端自动赋值（经销商类型且支付方式为空时才赋值）
     *                 代客操作：已核销，预收款
     *                 预收款账户：预收款核销，红冲预收款核销（退到余额）
     *                 线下打款：红冲预收款核销（原Pro的），红冲预收款余额
     *                 另外，红冲已核销与原单一致（关联新建时处理好）
     * @param platform 来源
     * @param data 数据data
     * @param sheetType 回款单类型
     * @param distributorMark 经销商标识
     * @return java.lang.String
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public String handlePayMethod4Distributor(String platform, JSONObject data, String sheetType, Integer distributorMark) {
        return SaveFormatHelp.handlePayMethod4Distributor(platform, data, sheetType, distributorMark);
    }

    /**
     * 经销商类型回款单，厂商端操作需后端自动赋值（经销商类型且支付方式为空时才赋值）
     *                 代客操作：已核销，预收款
     *                 预收款账户：预收款核销，红冲预收款核销（退到余额）
     *                 线下打款：红冲预收款核销（原Pro的），红冲预收款余额
     *                 另外，红冲已核销与原单一致（关联新建时处理好）
     * @param data 数据data
     * @param distributorMark 经销商标识
     * @param payMethod 支付方式
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void handlePayMethod4Distributor4SonSheet(JSONObject data, Integer distributorMark, String payMethod) {
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode()) && StringUtil.isNotEmpty(payMethod)) {
            data.put(PaymentSheetDistributorEnum.PAY_METHOD.getAttr(), payMethod);
        }
    }

    /**
     * 处理没有明细的情况：把父回/付款单内关联的数据置入相应容器内，以方便后续更新关联业务的数据（已收回/付款等）
     * @param data 回/付款单data
     * @param attr 字段名
     * @param sheetAmount 回/付款金额
     * @param idToAmount id和金额的对应关系map
     * @param ids 明细内数组的id集合
     * @throws XbbException
     * @author zcp
     * @date 19/12/4 004 16:24
     * @update 19/12/4 004 16:24
     * @since v1.0
     * @version v1.0
     */
    private void handleIdAmount4NoDetail(JSONObject data, String attr, Double sheetAmount, Map<Long, Double> idToAmount, Set<Long> ids) throws XbbException {
        List<Long> idList = getJsonArrOrDefaultFromFormData(data, attr, new JSONArray()).toJavaList(Long.class);
        if (idList.size() > BasicConstant.ONE) {
            //无明细，则最多只能一个合同、一个回/付款计划
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.NO_AMOUNT_DETAIL);
        }
        if (idList.size() > 0) {
            Long cid = idList.get(0);
            ids.add(cid);
            Double value = idToAmount.getOrDefault(cid, BasicConstant.ZERO_DOUBLE);
            value = Arith.add(value, sheetAmount);
            idToAmount.put(cid, value);
            data.put(attr, idList);
        }
    }

    /**
     * 处理回/付款单明细内的id和name，塞入相应集合
     * @param jsonObject 明细对象
     * @param attr 关联字段
     * @param linkedAttr 关联隐藏字段
     * @param detailIds 拼装所有明细的id数组
     * @param detailNames 拼装所有明细的名称数组
     * @param idToAmount id和金额的对应关系map
     * @param ids 明细内数组的id集合
     * @param amount 明细内金额
     * @throws XbbException
     * @author zcp
     * @date 19/11/21 021 20:09
     * @update 19/11/21 021 20:09
     * @since v1.0
     * @version v1.0
     */
    private void handleDetailIdName(JSONObject jsonObject, String attr, String linkedAttr, List<Long> detailIds, List<String> detailNames, Map<Long, Double> idToAmount, Set<Long> ids, Double amount) throws XbbException {
        List<Long> idList = getJsonArrOrDefaultFromFormData(jsonObject, attr, new JSONArray()).toJavaList(Long.class);
        if (CollectionsUtil.isEmpty(idList)) {
            return;
        }
        jsonObject.put(attr, idList);
        //明细内各个字段对应的数组内只有一条（为了给名称排重，所以判断detailIds不包含该id）
        Long id = idList.get(0);
        boolean flag = id != null && id != 0L && !detailIds.contains(id);
        if(flag){
            detailIds.add(id);
            JSONArray nameArr = getJsonArrFromFormData(jsonObject, linkedAttr);
            detailNames.add(nameArr.getString(0));
            ids.add(id);
        }
        //求金额时，要把金额汇总，如n条明细都是挂到同一个合同下，则需要把所有明细金额汇总得到该合同的回/付款额
        Double value = Arith.add(idToAmount.getOrDefault(id, 0D), amount);
        idToAmount.put(id, value);
    }

    /**
     * 保存时批次号必填校验
     * @param validateDataDTO 校验数据格式DTO
     * @throws XbbException
     */
    public void batchSaveRequiredCheck(ValidateDataDTO validateDataDTO) throws XbbException{
        SaveFormatHelp.batchSaveRequiredCheck(validateDataDTO);
    }

    /**
     * 标签字段处理
     * @param data
     * @param explainMap
     * @throws XbbException
     */
    public void dealLabelData(JSONObject data,  Map<String, FieldAttrEntity> explainMap) throws XbbException {
        // 标签处理
       SaveFormatHelp.dealLabelData(data, explainMap);
    }

    /**
     *
     * 获取回款计划执行人的id列表
     *
     * @param corpid
     * @param id
     * @param userId
     * @return
     */
    public List<String> saveExecutor2PaymentTaskUser(JSONObject data, String executor, String executorLinkedText, Long linkContractId, String corpid, Long id, String userId) throws XbbException {
        //执行人员
        List<String> thisUserIds = new ArrayList<>();
        if (data == null || executor == null || executorLinkedText == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
        }
        try {
            boolean isManager = false;
            boolean isCooper = false;
            boolean isCustom = false;
            JSONArray checkArray = data.getJSONArray(executor);
            for (Object obj : checkArray) {
                Integer check = Integer.parseInt(obj.toString());
                if (Objects.equals(check, ExecutorCheckBoxEnum.MAIN_USER_CHECK.getCode())) {
                    isManager = true;
                }
                if (Objects.equals(check, ExecutorCheckBoxEnum.COOPER_USER_CHECK.getCode())) {
                    isCooper = true;
                }
                if (Objects.equals(check, ExecutorCheckBoxEnum.CUSTOM_CHECK.getCode())) {
                    isCustom = true;
                }
            }
            List<String> mainUserIdList = new ArrayList<>();
            List<String> coUserIdList = new ArrayList<>();
            if (isManager || isCooper) {
                //查找所有合同的协同团队
                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                param.put("del", 0);
                param.put("contractId", linkContractId);
                param.put("corpid", corpid);
                List<ContractUserEntity> contractUserList = contractUserModel.findEntitys(param);
                for (ContractUserEntity contractUserEntity : contractUserList) {
                    String cUserId = contractUserEntity.getUserId();
                    Integer isMain = contractUserEntity.getIsMain() == null ? 0 : contractUserEntity.getIsMain();
                    if (BasicConstant.ONE.equals(isMain)) {
                        mainUserIdList.add(cUserId);
                    }else {
                        coUserIdList.add(cUserId);
                    }
                }
            }

            JSONArray userArray = data.getJSONArray(executorLinkedText);
            if (isCustom && userArray != null) {
                for (Object userObj : userArray) {
                    String customUserId = userObj.toString();
                    thisUserIds.add(customUserId);
                }
            }
            // 负责人
            if (isManager) {
                thisUserIds.addAll(mainUserIdList);
            }
            // 协同人
            if (isCooper) {
                thisUserIds.addAll(coUserIdList);
            }
            // 保存
            if (!thisUserIds.isEmpty()) {
                //对用户进行去重
                thisUserIds = thisUserIds.stream().distinct().collect(Collectors.toList());
                Map<String, Object> userParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                userParam.clear();
                userParam.put("del", 0);
                userParam.put("corpid", corpid);
                userParam.put("userIdIn", thisUserIds);
                List<UserEntity> users = userModel.findEntitys(userParam);
                //回款计划执行人新增
                List<PaymentTaskUserEntity> paymentTaskUserEntities = new ArrayList<>();
                for (UserEntity user : users) {
                    PaymentTaskUserEntity planUser = new PaymentTaskUserEntity();
                    planUser.setCorpid(corpid);
                    planUser.setDataId(id);
                    planUser.setUserId(user.getUserId());
                    planUser.setUserName(user.getName());
                    planUser.setUserAvatar(user.getAvatar());
                    paymentTaskUserEntities.add(planUser);
                }
                paymentTaskUserModel.insertBatch(paymentTaskUserEntities);
            }
        } catch (Exception e) {
            LOG.error("保存执行人到计划协同团队表出错:",e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
        }
        return thisUserIds;
    }

    /**
     * 解析数据
     *
     * @param data 数据
     * @param attr 字段解释
     * @param value 对最底层的key进行处理
     */
    public void formatJsonData(JSONObject data, String attr, String value) {
        if (!Objects.isNull(data)) {
            JSONObject jsonObject = (JSONObject) data.get(attr);
            if (!Objects.isNull(jsonObject)) {
                Object obj = jsonObject.get(value);
                String str = "";
                if (obj instanceof String) {
                    str = (String)jsonObject.get(value);
                    data.put(attr,str);
                }else if (obj instanceof Integer) {
                    str = ((Integer)jsonObject.get(value)).toString();
                    data.put(attr,str);
                }
            }
        }
    }
}




